﻿@using Radzen
@using System.Collections
@using System.Collections.Generic
@using System.Linq.Dynamic.Core
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.JSInterop
@typeparam TValue
@inherits DropDownBase<TValue>

@if (Visible)
{
    <div @ref="@Element" @attributes="Attributes" class="@GetCssClass()"
         style="@Style" tabindex="@TabIndex" id="@GetId()">
        <div class="rz-helper-hidden-accessible">
            <input disabled="@Disabled" style="width:100%" aria-haspopup="listbox" readonly="" type="text"
                   name="@Name" aria-label="@(!Multiple && Value != null ? PropertyAccess.GetItemOrValueFromProperty(Value, TextProperty) : "")" />

        </div>

        @if (Template != null && selectedItem != null)
        {
            <label class="rz-dropdown-label rz-inputtext " onclick="@OpenPopupScript()" style="width:100%;">
                @Template(selectedItem)
            </label>
        }
        else if (selectedItem != null)
        {
            <label class="rz-dropdown-label rz-inputtext " onclick="@OpenPopupScript()" style="width:100%;">
                @PropertyAccess.GetItemOrValueFromProperty(selectedItem, TextProperty)
            </label>
        }
        else if (selectedItems.Count > 0)
        {
            <label class="rz-dropdown-label rz-inputtext " onclick="@OpenPopupScript()" style="width:100%;">
                @if (selectedItems.Count < MaxSelectedLabels)
                {
                    @if (Template == null)
                    {
                        @(string.Join(",", selectedItems.Select(i => PropertyAccess.GetItemOrValueFromProperty(i, TextProperty))))
                    }
                    else
                    {
                        foreach (var item in selectedItems)
                        {
                            @Template(item)@(",")
                        }
                    }
                }
                else
                {
                    @($"{selectedItems.Count} {SelectedItemsText}")
                }
            </label>
        }
        else if (SelectedValue != null)
        {
            <label class="rz-dropdown-label rz-inputtext rz-corner-all" onclick="@OpenPopupScript()" style="width:100%;">
                @PropertyAccess.GetItemOrValueFromProperty(SelectedValue, TextProperty)
            </label>
        }
        else if (!string.IsNullOrEmpty(Placeholder))
        {
            <label class="rz-dropdown-label rz-inputtext  rz-placeholder" onclick="@OpenPopupScript()" style="width:100%;">
                @Placeholder
            </label>
        }
        else
        {
            <label class="rz-dropdown-label rz-inputtext " onclick="@OpenPopupScript()" style="width:100%">
                &nbsp;
            </label>
        }

        <div class="rz-dropdown-trigger  rz-corner-right" onclick="@OpenPopupScript()">
            <span class="rz-dropdown-trigger-icon  rzi rzi-chevron-down"></span>
        </div>
        <div id="@PopupID" class="@(Multiple ? "rz-multiselect-panel" : "rz-dropdown-panel")"
             style="display:none;min-width:320px;padding:0px;">
            <div class="rz-lookup-panel">
                @if (AllowFiltering)
                {
                    <div class="rz-lookup-search">
                        <input id="@SearchID" @ref="@search" tabindex="-1" placeholder="@SearchText"
                               @onchange="@((args) => OnFilter(args))" @onkeydown="@((args) => OnFilterKeyPress(args))" value="@searchText" style="@(ShowSearch ? "" : "margin-right:0px;")" />
                        @if (ShowSearch)
                        {
                            <button class="rz-button rz-button-md    btn-primary rz-button-icon-only" type="button" title="">
                                <i class="rz-button-icon-left rzi">search</i>
                            </button>
                        }
                    </div>
                }
                @if (Template != null)
                {
                    <RadzenDataGrid ColumnWidth="@ColumnWidth" @ref="grid" Data="@(LoadData.HasDelegate ? (Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()) : pagedData)" Count="@(LoadData.HasDelegate ? Count : count)" LoadData="@OnLoadData"
                                    TItem="object" PageSize="@PageSize" PageNumbersCount="@PageNumbersCount" AllowPaging="@(!IsVirtualizationAllowed())" AllowSorting="@AllowSorting" RowSelect="@OnRowSelect" Style="@(IsVirtualizationAllowed() ? "height:285px" : "")">
                        <Columns>
                            <RadzenDataGridColumn TItem="object" Property="@TextProperty" Title="@TextProperty" Type="typeof(string)">
                                <Template>
                                    @Template(context)
                                </Template>
                            </RadzenDataGridColumn>
                        </Columns>
                    </RadzenDataGrid>
                }
                else
                {
                    if (!string.IsNullOrEmpty(TextProperty) || Columns != null)
                    {
                        <RadzenDataGrid ColumnWidth="@ColumnWidth" EmptyText="@EmptyText" @ref="grid" Data="@(LoadData.HasDelegate ? (Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()) : pagedData)" Count="@(LoadData.HasDelegate ? Count : count)" LoadData="@OnLoadData"
                                        TItem="object" PageSize="@PageSize" PageNumbersCount="@PageNumbersCount" AllowPaging="@(!IsVirtualizationAllowed())" AllowSorting="@AllowSorting" RowSelect="@OnRowSelect" Style="@(IsVirtualizationAllowed() ? "height:285px" : "")">
                            <Columns>
                                @if (Columns != null)
                                {
                                    @Columns
                                }
                                else
                                {
                                    <RadzenDataGridColumn TItem="object" Property="@TextProperty" Title="@TextProperty" Type="typeof(string)" />
                                }
                            </Columns>
                        </RadzenDataGrid>
                    }
                    else
                    {
                        <RadzenDataGrid ColumnWidth="@ColumnWidth" EmptyText="@EmptyText" @ref="grid" Data="@(LoadData.HasDelegate ? (Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()) : pagedData)" Count="@(LoadData.HasDelegate ? Count : count)" LoadData="@OnLoadData"
                                        TItem="object" PageSize="@PageSize" PageNumbersCount="@PageNumbersCount" AllowPaging="@(!IsVirtualizationAllowed())" AllowSorting="@AllowSorting" RowSelect="@OnRowSelect" Style="@(IsVirtualizationAllowed() ? "height:285px" : "")">
                            <Columns>
                                <RadzenDataGridColumn TItem="object" Property="it" Title="Item" Type="typeof(string)">
                                    <Template>
                                        @context
                                    </Template>
                                </RadzenDataGridColumn>
                            </Columns>
                        </RadzenDataGrid>
                    }
                }
            </div>
        </div>
        @if (AllowClear && (!Multiple && HasValue || Multiple && selectedItems.Count > 0))
        {
            <i class="rz-dropdown-clear-icon rzi rzi-times" @onclick="@Clear"></i>
        }
    </div>
}
@code {
    [Parameter]
    public string ColumnWidth { get; set; }

    [Parameter]
    public bool Responsive { get; set; } = true;

    [Parameter]
    public bool ShowSearch { get; set; } = true;

    [Parameter]
    public int PageNumbersCount { get; set; } = 2;

    [Parameter]
    public string EmptyText { get; set; } = "No records to display.";

    [Parameter]
    public string SearchText { get; set; } = "Search...";

    [Parameter]
    public object SelectedValue { get; set; }

    [Parameter]
    public RenderFragment Columns { get; set; }

    RadzenDataGrid<object> grid;

    IEnumerable<object> pagedData;
    int count;

    [Parameter]
    public int MaxSelectedLabels { get; set; } = 4;

#if !NET5
    [Parameter]
    public int PageSize { get; set; } = 5;

    [Parameter]
    public int Count { get; set; }
#endif

    [Parameter]
    public string SelectedItemsText { get; set; } = "items selected";

    protected ElementReference popup;

    protected override Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
#if NET5
            if (grid != null)
            {
                grid.SetAllowVirtualization(AllowVirtualization);
            }
#endif            
            if(Visible && LoadData.HasDelegate && Data == null)
            {
                LoadData.InvokeAsync(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize });
            }

            StateHasChanged();
        }

        return base.OnAfterRenderAsync(firstRender);
    }

    protected override void OnDataChanged()
    {
        base.OnDataChanged();

        if (!LoadData.HasDelegate)
        {
            searchText = null;
            OnLoadData(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize }).Wait();
        }
    }

    public void Reload()
    {
        searchText = null;
        OnLoadData(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize }).Wait();
    }

    private string GetPropertyFilterExpression(string property, string filterCaseSensitivityOperator)
    {
        if (property == null)
        {
            property = "it";
        }
        var p = $@"({property} == null ? """" : {property})";
        return $"{p}{filterCaseSensitivityOperator}.{Enum.GetName(typeof(StringFilterOperator), FilterOperator)}(@0)";
    }

    private bool IsColumnFilterPropertyTypeString(RadzenDataGridColumn<object> column)
    {
        var property = column.GetFilterProperty();
        var itemType = Data != null ? Data.AsQueryable().ElementType : typeof(object);
        var type = PropertyAccess.GetPropertyType(itemType, property);

        return type == typeof(string);
    }

    async Task OnLoadData(LoadDataArgs args)
    {
        if (!LoadData.HasDelegate)
        {
            var query = Query;

            if (query == null)
                return;

            if (!string.IsNullOrEmpty(searchText))
            {
                string filterCaseSensitivityOperator = FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? ".ToLower()" : "";

                if (AllowFilteringByAllStringColumns)
                {
                    query = query.Where(string.Join(" || ", grid.ColumnsCollection.Where(c => c.Filterable && IsColumnFilterPropertyTypeString(c))
                        .Select(c => GetPropertyFilterExpression(c.GetFilterProperty(), filterCaseSensitivityOperator))),
                            FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
                }
                else
                {
                    query = query.Where($"{GetPropertyFilterExpression(TextProperty, filterCaseSensitivityOperator)}",
                        FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
                }
            }

            if (!string.IsNullOrEmpty(args.OrderBy))
            {
                query = query.OrderBy(args.OrderBy);
            }

            _internalView = query;

            count = query.Count();

            pagedData = QueryableExtension.ToList(query.Skip(args.Skip.HasValue ? args.Skip.Value : 0).Take(args.Top.HasValue ? args.Top.Value : PageSize)).Cast<object>();
        }
        else
        {
            await LoadData.InvokeAsync(new Radzen.LoadDataArgs() { Skip = args.Skip, Top = args.Top, OrderBy = args.OrderBy, Filter = searchText });
        }
    }

    IEnumerable _internalView = Enumerable.Empty<object>();
    public new IEnumerable View
    {
        get
        {
            return _internalView;
        }
    }

    protected override IEnumerable<object> Items
    {
        get
        {
            return pagedData != null ? pagedData : Enumerable.Empty<object>();
        }
    }

    protected override void SelectItemFromValue(object value)
    {
        if (value != null && Query != null)
        {
            if (!Multiple)
            {
                if (!string.IsNullOrEmpty(ValueProperty))
                {
                    SelectedItem = Query.Where($@"{ValueProperty} == @0", value).FirstOrDefault();
                }
                else
                {
                    selectedItem = Value;
                }
                SelectedItemChanged?.Invoke(selectedItem);
            }
            else
            {
                var values = value as dynamic;
                if (values != null)
                {
                    if (!string.IsNullOrEmpty(ValueProperty))
                    {
                        foreach (object v in values)
                        {
                            var item = Query.Where($@"{ValueProperty} == @0", v).FirstOrDefault();
                            if (item != null && selectedItems.IndexOf(item) == -1)
                            {
                                selectedItems.Add(item);
                            }
                        }
                    }
                    else
                    {
                        selectedItems.AddRange(values);
                    }

                }
            }
        }
        else
        {
            selectedItem = null;
        }
    }

    async System.Threading.Tasks.Task Clear()
    {
        if (Disabled)
            return;

        searchText = null;
        Value = default(TValue);
        selectedItem = null;

        selectedItems.Clear();

        await ValueChanged.InvokeAsync((TValue)Value);
        if (FieldIdentifier.FieldName != null) { EditContext?.NotifyFieldChanged(FieldIdentifier); }
        await Change.InvokeAsync(Value);

        await grid.Reload();

        StateHasChanged();
    }

    string previousSearch;
    protected override async Task OnFilterKeyPress(KeyboardEventArgs args)
    {
        var items = (LoadData.HasDelegate ? Data != null ? Data : Enumerable.Empty<object>() : (pagedData != null ? pagedData : Enumerable.Empty<object>())).OfType<object>().ToList();

        var key = args.Code != null ? args.Code : args.Key;

        if (key == "ArrowDown" || key == "ArrowUp")
        {
            try
            {
                if (key == "ArrowDown" && selectedIndex < items.Count - 1)
                {
                    selectedIndex++;
                }

                if (key == "ArrowUp" && selectedIndex > 0)
                {
                    selectedIndex--;
                }

                var item = items.ElementAtOrDefault(selectedIndex);

                if (item != null && (!Multiple ? selectedItem != item : true))
                {
                    await grid.OnRowSelect(item, false);
                }
            }
            catch (Exception)
            {
                //
            }
        }
        else if (key == "Enter")
        {
            var item = items.ElementAtOrDefault(selectedIndex);
            if (item != null && (!Multiple ? selectedItem != item : true))
            {
                await OnRowSelect(item);
            }
        }
        else if (key == "Escape")
        {
            await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
        }
        else
        {
            selectedIndex = -1;
            Debounce(DebounceFilter, FilterDelay);
        }
    }

    async Task DebounceFilter()
    {
        searchText = await JSRuntime.InvokeAsync<string>("Radzen.getInputValue", search);
        if (searchText != previousSearch)
        {
            previousSearch = searchText;
            _view = null;

            await InvokeAsync(() =>
            {
#if NET5
                if (grid?.virtualize != null)
                {
                    if(string.IsNullOrEmpty(searchText))
                    {
                        if(LoadData.HasDelegate)
                        {
                            Data = null;
                        }
                        else
                        {
                            pagedData = null;
                            StateHasChanged();
                        }
                    }
                    grid.virtualize.RefreshDataAsync().Wait();
                }
#endif 
                 StateHasChanged();
                 grid.FirstPage(true).Wait();

            });
        }
    }

    protected override async Task OnFilter(ChangeEventArgs args)
    {
        await DebounceFilter();
    }

    [Parameter]
    public bool AllowSorting { get; set; } = true;

    [Parameter]
    public override bool AllowFiltering { get; set; } = true;

    [Parameter]
    public bool AllowFilteringByAllStringColumns { get; set; }

    async Task OnRowSelect(object item)
    {
        await SelectItem(item);
        if (!Disabled && !Multiple)
        {
            await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
        }
    }

    protected override string GetComponentCssClass()
    {
        var classList = new List<string>();

        classList.Add("rz-dropdown");

        if (FieldIdentifier.FieldName != null && EditContext != null)
        {
            classList.Add(EditContext?.FieldCssClass(FieldIdentifier));
        }

        if (Disabled)
        {
            classList.Add("rz-state-disabled");
        }

        if (AllowClear)
        {
            classList.Add("rz-clear");
        }

        return String.Join(" ", classList);
    }

    public override void Dispose()
    {
        base.Dispose();

        if (IsJSRuntimeAvailable)
        {
            JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", PopupID);
        }
    }
}
